<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>service.js - Documentation</title>

    <script src="scripts/prettify/prettify.js"></script>
    <script src="scripts/prettify/lang-css.js"></script>
    <!--[if lt IE 9]>
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <link type="text/css" rel="stylesheet" href="styles/prettify.css">
    <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
</head>
<body>

<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
  <div class="navicon"></div>
</label>

<label for="nav-trigger" class="overlay"></label>

<nav>
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Git.html">Git</a><ul class='methods'><li data-type='method'><a href="Git.html#.close">close</a></li><li data-type='method'><a href="Git.html#.create">create</a></li><li data-type='method'><a href="Git.html#.exists">exists</a></li><li data-type='method'><a href="Git.html#.handle">handle</a></li><li data-type='method'><a href="Git.html#.list">list</a></li><li data-type='method'><a href="Git.html#.listen">listen</a></li><li data-type='method'><a href="Git.html#.mkdir">mkdir</a></li></ul></li><li><a href="HttpDuplex.html">HttpDuplex</a><ul class='methods'><li data-type='method'><a href="HttpDuplex.html#addTrailers">addTrailers</a></li><li data-type='method'><a href="HttpDuplex.html#cork">cork</a></li><li data-type='method'><a href="HttpDuplex.html#destroy">destroy</a></li><li data-type='method'><a href="HttpDuplex.html#end">end</a></li><li data-type='method'><a href="HttpDuplex.html#getHeader">getHeader</a></li><li data-type='method'><a href="HttpDuplex.html#pause">pause</a></li><li data-type='method'><a href="HttpDuplex.html#removeHeader">removeHeader</a></li><li data-type='method'><a href="HttpDuplex.html#resume">resume</a></li><li data-type='method'><a href="HttpDuplex.html#setDefaultEncoding">setDefaultEncoding</a></li><li data-type='method'><a href="HttpDuplex.html#setEncoding">setEncoding</a></li><li data-type='method'><a href="HttpDuplex.html#setHeader">setHeader</a></li><li data-type='method'><a href="HttpDuplex.html#uncork">uncork</a></li><li data-type='method'><a href="HttpDuplex.html#write">write</a></li><li data-type='method'><a href="HttpDuplex.html#writeContinue">writeContinue</a></li><li data-type='method'><a href="HttpDuplex.html#writeHead">writeHead</a></li><li data-type='method'><a href="HttpDuplex.html#writeHeader">writeHeader</a></li></ul></li><li><a href="Service.html">Service</a><ul class='methods'><li data-type='method'><a href="Service.html#.accept">accept</a></li><li data-type='method'><a href="Service.html#.reject">reject</a></li></ul></li></ul><h3>Modules</h3><ul><li><a href="module-lib_util.html">lib/util</a><ul class='methods'><li data-type='method'><a href="module-lib_util.html#~basicAuth">basicAuth</a></li><li data-type='method'><a href="module-lib_util.html#~createAction">createAction</a></li><li data-type='method'><a href="module-lib_util.html#~infoResponse">infoResponse</a></li><li data-type='method'><a href="module-lib_util.html#~noCache">noCache</a></li><li data-type='method'><a href="module-lib_util.html#~onExit">onExit</a></li><li data-type='method'><a href="module-lib_util.html#~parseGitName">parseGitName</a></li><li data-type='method'><a href="module-lib_util.html#~serviceRespond">serviceRespond</a></li></ul></li></ul><h3>Events</h3><ul><li><a href="HttpDuplex.html#event:close">close</a></li><li><a href="HttpDuplex.html#event:data">data</a></li><li><a href="HttpDuplex.html#event:drain">drain</a></li><li><a href="HttpDuplex.html#event:end">end</a></li><li><a href="HttpDuplex.html#event:error">error</a></li><li><a href="Git.html#event:fetch">fetch</a></li><li><a href="Git.html#event:head">head</a></li><li><a href="Git.html#event:info">info</a></li><li><a href="Git.html#event:push">push</a></li><li><a href="Git.html#event:tag">tag</a></li></ul><h3>Global</h3><ul><li><a href="global.html#getType">getType</a></li></ul>
</nav>

<div id="main">
    
    <h1 class="page-title">service.js</h1>
    

    



    
    <section>
        <article>
            <pre class="prettyprint source linenums"><code>const through = require('through');
const zlib = require('zlib');
const util = require('util');
const os = require('os');
const { spawn } = require('child_process');

const HttpDuplex = require('./http-duplex');

const headerRE = {
  'receive-pack': '([0-9a-fA-F]+) ([0-9a-fA-F]+) refs\/(heads|tags)\/(.*?)( |00|\u0000)|^(0000)$', // eslint-disable-line
  'upload-pack': '^\\S+ ([0-9a-fA-F]+)'
};

const packSideband = s => {
  const n = (4 + s.length).toString(16);
  return Array(4 - n.length + 1).join('0') + n + s;
};

class Service extends HttpDuplex {
  /**
   * Handles invoking the git-*-pack binaries
   * @class Service
   * @extends HttpDuplex
   * @param  {Object}               opts - options to bootstrap the service object
   * @param  {http.IncomingMessage }   req  - http request object
   * @param  {http.ServerResponse}  res  - http response
   */
  constructor(opts, req, res) {
    super(req, res);

    var data = '';
    var self = this;

    this.status = 'pending';
    this.repo = opts.repo;
    this.service = opts.service;
    this.cwd = opts.cwd;
    this.logs = [];

    var buffered = through().pause();

    // stream needed to receive data after decoding, but before accepting
    var ts = through();

    var decoder = {
        'gzip': () => zlib.createGunzip(),
        'deflate': () => zlib.createDeflate()
    }[req.headers['content-encoding']];

    if (decoder) {
        // data is compressed with gzip or deflate
        req.pipe(decoder()).pipe(ts).pipe(buffered);
    } else {
        // data is not compressed
        req.pipe(ts).pipe(buffered);
    }

    if(req.headers["authorization"]) {
      const tokens = req.headers["authorization"].split(" ");
      if (tokens[0] === "Basic") {
          const splitHash = Buffer.from(tokens[1], 'base64').toString('utf8').split(":");
          this.username = splitHash.shift();
      }
    }

    ts.once('data', function onData(buf) {
        data += buf;

        var ops = data.match(new RegExp(headerRE[self.service], 'gi'));
        if (!ops) return;
        data = undefined;

        ops.forEach(function(op) {
            var type;
            var m = op.match(new RegExp(headerRE[self.service]));

            if (self.service === 'receive-pack') {
                self.last = m[1];
                self.commit = m[2];

                if (m[3] == 'heads') {
                    type = 'branch';
                    self.evName = 'push';
                } else {
                    type = 'version';
                    self.evName = 'tag';
                }

                var headers = {
                    last: self.last,
                    commit: self.commit
                };
                headers[type] = self[type] = m[4];
                self.emit('header', headers);
            } else if (self.service === 'upload-pack') {
                self.commit = m[1];
                self.evName = 'fetch';
                self.emit('header', {
                    commit: self.commit
                });
            }
        });
    });

    self.once('accept', function onAccept() {
        process.nextTick(function() {
            const cmd = os.platform() == 'win32' ?
              ['git', opts.service, '--stateless-rpc', opts.cwd]
            :
              ['git-' + opts.service, '--stateless-rpc', opts.cwd];

            const ps = spawn(cmd[0], cmd.slice(1));

            ps.on('error', function(err) {
              self.emit('error', new Error(`${err.message} running command ${cmd.join(' ')}`));
            });

            self.emit('service', ps);

            var respStream = through(function write(c) {
                if (self.listeners('response').length === 0) {
                  if(self.logs.length > 0) {
                    while(self.logs.length > 0) {
                      this.queue(self.logs.pop());
                    }
                  }

                  return this.queue(c);
                }
                // prevent git from sending the close signal
                if (c.length === 4 &amp;&amp; c.toString() === '0000') return;
                this.queue(c);
            }, function end() {
                if (self.listeners('response').length > 0) return;

                this.queue(null);
            });

            respStream.log = function() {
              self.log(...arguments);
            };

            self.emit('response', respStream, function endResponse() {
                res.queue(Buffer.from('0000'));
                res.queue(null);
            });

            ps.stdout.pipe(respStream).pipe(res);

            buffered.pipe(ps.stdin);
            buffered.resume();

            ps.on('exit', () => {
              if(self.logs.length > 0) {
                while(self.logs.length > 0) {
                  respStream.queue(self.logs.pop());
                }
                respStream.queue(Buffer.from('0000'));
                respStream.queue(null);
              }

              self.emit.bind(self, 'exit');
            });
        });
    });

    self.once('reject', function onReject(code, msg) {
        res.statusCode = code;
        res.end(msg);
    });
  }

  log() {
    const _log = util.format(...arguments);
    const SIDEBAND = String.fromCharCode(2); // PROGRESS
    const message = `${SIDEBAND}${_log}\n`;
    const formattedMessage = Buffer.from(packSideband(message));

    this.logs.unshift(formattedMessage);
  }
  /**
   * reject request in flight
   * @method reject
   * @memberof Service
   * @param  {Number} code - http response code
   * @param  {String} msg  - message that should be displayed on teh client
   */
  reject(code, msg) {
      if (this.status !== 'pending') return;

      if (msg === undefined &amp;&amp; typeof code === 'string') {
          msg = code;
          code = 500;
      }
      this.status = 'rejected';
      this.emit('reject', code || 500, msg);
  }
  /**
   * accepts request to access resource
   * @method accept
   * @memberof Service
   */
  accept() {
      if (this.status !== 'pending') return;

      this.status = 'accepted';
      this.emit('accept');
  }
}

module.exports = Service;
</code></pre>
        </article>
    </section>




</div>

<br class="clear">

<footer>
    Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>

<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
</body>
</html>
